package com.flow.asyncflow.core.utils.log.asyncflowlog;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.flow.asyncflow.core.entity.config.AsyncFlowInfo;
import com.flow.asyncflow.core.entity.core.AsyncFlowBaseContext;
import com.flow.asyncflow.core.entity.core.AsyncFlowNodeResponse;
import com.flow.asyncflow.core.entity.core.AsyncFlowRequest;
import com.flow.asyncflow.core.entity.log.asyncflowlog.AsyncFlowExecutionMethodEntity;
import com.flow.asyncflow.core.entity.log.asyncflowlog.AsyncFlowLogEntity;
import com.flow.asyncflow.core.entity.log.asyncflowlog.AsyncFlowNodeDetailLogEntity;
import com.flow.asyncflow.core.entity.log.asyncflowlog.AsyncFlowNodeLogEntity;
import com.flow.asyncflow.core.enums.ExecuteEnum;
import com.flow.asyncflow.core.utils.common.UtilsCommon;
import com.flow.asyncflow.core.utils.http.AsyncFlowHttpClientUtil;
import com.flow.asyncflow.core.utils.mongodb.MongoHelper;
import com.flow.asyncflow.core.utils.msg.AsyncFlowSendMsg;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.bson.Document;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

public class AsyncFlowCommon_Log {


    //region 添加操作节点执行日志

    /**
     * 执行过程中添加操作节点执行日志
     *
     * @param request               请求参数
     * @param asyncFlowNodeResponse 返回参数
     * @param asyncFlowBaseContext  上下文
     * @param obj                   执行体
     * @param startTime             开始执行时间
     * @param executeEnum           日志类型 1：验证   2：查询  3：操作   4：保存    5：保存后同步    6：保存后异步
     * @param isAsync               方法运行方式 是否异步
     */
    public static void AddAsyncFlowLog(AsyncFlowRequest request, AsyncFlowNodeResponse asyncFlowNodeResponse, AsyncFlowBaseContext asyncFlowBaseContext, Object obj, Date startTime, ExecuteEnum executeEnum, boolean isAsync) {
        try {
            //节点内部标记不记录当前节点的轨迹日志
            //异步流配置了当前环境不记录轨迹日志
            //在请求执行中设置了不记录本次请求的轨迹日志
            if (asyncFlowNodeResponse.isIgnoreNodeTrackLog()
                    || (asyncFlowBaseContext.getAsyncFlowInfo().getIgnoreFlowLogEvn().contains(asyncFlowBaseContext.getAsyncFlowInfo().getEvn()))
                    || asyncFlowBaseContext.isIgnoreThisReqFlowLog()) {
                return;
            }
            AsyncFlowExecutionMethodEntity asyncFlowExecutionMethodEntity = UtilsCommon.getLogMethodName(obj);
            AsyncFlowNodeLogEntity asyncFlowNodeLog = new AsyncFlowNodeLogEntity();
            asyncFlowNodeLog.setBusinessSerialId(request.getBusinessSerialId());
            asyncFlowNodeLog.setAsyncFlowModuleEnum(request.getReqTypeEnumName());
            asyncFlowNodeLog.setAsyncFlowNodeEnum(request.getReqTypeEnumMsg());
            asyncFlowNodeLog.setLogMethodName(asyncFlowExecutionMethodEntity.getLogMethodName());
            asyncFlowNodeLog.setLogFileName(asyncFlowExecutionMethodEntity.getMethodSimpleName());
            asyncFlowNodeLog.setLogNodePath(asyncFlowExecutionMethodEntity.getMethodName());
            asyncFlowNodeLog.setLogMsg(asyncFlowNodeResponse.getMsg());
            asyncFlowNodeLog.setLogTime(LocalDateTime.now());
            asyncFlowNodeLog.setCode(asyncFlowNodeResponse.getCode());
            asyncFlowNodeLog.setAsync(isAsync);
            Long time = new Date().getTime() - startTime.getTime();
            asyncFlowNodeLog.setUseTimes(time);
            asyncFlowNodeLog.setExecuteEnumCode(executeEnum.getCode());
            String asyncFlowLogName = asyncFlowNodeLog.getLogFileName();
            List<AsyncFlowNodeDetailLogEntity> asyncFlowNodeDetailLogEntityList = asyncFlowBaseContext.getAsyncFlowNodeDetailLogEntityListMap().get(asyncFlowNodeLog.getLogFileName());
            if (asyncFlowNodeDetailLogEntityList != null && asyncFlowNodeDetailLogEntityList.size() > 0) {
                asyncFlowNodeLog.getAsyncFlowNodeDetailLogEntityList().addAll(asyncFlowNodeDetailLogEntityList);
            }
            asyncFlowBaseContext.getAsyncFlowNodeLogEntityList().add(asyncFlowNodeLog);
        } catch (Exception e) {
            String msg = AsyncFlowSendMsg.buildErrorMsg(request, e, obj.getClass().getSimpleName(), asyncFlowBaseContext.getAsyncFlowInfo());
            AsyncFlowSendMsg.sendQYWXMsg(asyncFlowBaseContext.getAsyncFlowInfo().getFlowMaintainName(), msg, asyncFlowBaseContext.getAsyncFlowInfo().getNodeArmUrl());

        }

    }
    //endregion

    //region 插入节点明细日志

    /**
     * 插入节点明细日志
     *
     * @param asyncFlowBaseContext 上下文
     * @param logFileName          文件名称
     * @param LogDetailName        日志名称
     * @param request              请求
     * @param response             返回
     * @param status               状态
     */
    public static void AddAsyncFlowDetailLog(AsyncFlowBaseContext asyncFlowBaseContext, String logFileName, String LogDetailName, Object request, Object response, boolean... status) {

        try {
            if (asyncFlowBaseContext == null) {
                return;
            }
            //异步流配置了当前环境不记录轨迹日志
            //在请求执行中设置了不记录本次请求的轨迹日志
            if (asyncFlowBaseContext.getAsyncFlowInfo().getIgnoreFlowLogEvn().contains(asyncFlowBaseContext.getAsyncFlowInfo().getEvn()) || asyncFlowBaseContext.isIgnoreThisReqFlowLog()) {
                return;
            }
            AsyncFlowNodeDetailLogEntity asyncFlowLogDetailEntity = new AsyncFlowNodeDetailLogEntity();
            //日志所在文件类名称
            asyncFlowLogDetailEntity.setLogFileName(logFileName);
            asyncFlowLogDetailEntity.setLogDetailName(LogDetailName);
            String reqLog = "";
            String resLog = "";
            if (request != null) {
                if (request instanceof String) {
                    reqLog = (String) request;
                } else {
                    reqLog = JSON.toJSONString(request, SerializerFeature.DisableCircularReferenceDetect);
                }
            }
            if (response != null) {
                if (response instanceof String) {
                    resLog = (String) response;
                } else {
                    resLog = JSON.toJSONString(response, SerializerFeature.DisableCircularReferenceDetect);
                }
            }
            asyncFlowLogDetailEntity.setRequest(reqLog);
            asyncFlowLogDetailEntity.setResponse(resLog);
            if (status.length > 0) {
                asyncFlowLogDetailEntity.setStatus(status[0]);
            }
            List<AsyncFlowNodeDetailLogEntity> asyncFlowNodeDetailLogEntityList = asyncFlowBaseContext.getAsyncFlowNodeDetailLogEntityListMap().get(logFileName);
            if (asyncFlowNodeDetailLogEntityList == null) {
                asyncFlowNodeDetailLogEntityList = new ArrayList<>();
            }
            asyncFlowNodeDetailLogEntityList.add(asyncFlowLogDetailEntity);
            asyncFlowBaseContext.getAsyncFlowNodeDetailLogEntityListMap().put(logFileName, asyncFlowNodeDetailLogEntityList);
        } catch (Exception ex) {
            String msg = AsyncFlowSendMsg.buildErrorMsg(asyncFlowBaseContext.getAsyncFlowRequest(AsyncFlowRequest.class), ex, logFileName, asyncFlowBaseContext.getAsyncFlowInfo());
            AsyncFlowSendMsg.sendQYWXMsg(asyncFlowBaseContext.getAsyncFlowInfo().getFlowMaintainName(), msg, asyncFlowBaseContext.getAsyncFlowInfo().getNodeArmUrl());
        }

    }


    //endregion

    //region 执行完毕后保存节点执行日志

    /**
     * 执行完毕后保存节点执行日志
     *
     * @param request              请求参数
     * @param asyncFlowBaseContext 上下文
     * @param response             返回参数
     * @param timeConsuming        耗时详细
     */

    public static void SaveExecuteTrackLogs(Object request, AsyncFlowBaseContext asyncFlowBaseContext, Object response, LinkedHashMap<ExecuteEnum, Long> timeConsuming, boolean allNodeIsSuccess, AsyncFlowInfo asyncFlowInfo) {

        //异步流配置了当前环境不记录轨迹日志，直接跳出
        //在请求执行中设置了不记录本次请求的轨迹日志，直接跳出
        if ((asyncFlowBaseContext.getAsyncFlowInfo().getIgnoreFlowLogEvn().contains(asyncFlowBaseContext.getAsyncFlowInfo().getEvn()))
                || asyncFlowBaseContext.isIgnoreThisReqFlowLog()) {
            return;
        }
        AsyncFlowRequest asyncFlowRequest = asyncFlowBaseContext.getAsyncFlowRequest(AsyncFlowRequest.class);
        boolean errorRequest = false;
        //总耗时和耗时明细组装
        String uptimeDes;
        long uptime = 0;
        StringBuilder stringBuilderUptimeDes = new StringBuilder();

        for (Map.Entry<ExecuteEnum, Long> entry : timeConsuming.entrySet()) {
            long time = entry.getValue();
            stringBuilderUptimeDes.append("【").append(entry.getKey().getMsg()).append("--").append(time).append("ms").append("】");
            uptime = uptime + time;
        }
        uptimeDes = "【总耗时--" + uptime + "ms】" + stringBuilderUptimeDes.toString();
        //保存流程运行轨迹
        AsyncFlowLogEntity asyncFlowLogEntity = new AsyncFlowLogEntity();
        try {
            //请求参数中没有给唯一号，自动生成一个默认的唯一号
            if (StringUtils.isBlank(asyncFlowRequest.getBusinessSerialId())) {
                errorRequest = true;
                String dateGuid = LocalDateTimeUtil.format(asyncFlowRequest.getRequesterTime(), DatePattern.PURE_DATETIME_PATTERN);
                asyncFlowLogEntity.setBusinessSerialId(asyncFlowRequest.getReqTypeEnumMsg() + "-" + dateGuid);
            } else {
                asyncFlowLogEntity.setBusinessSerialId(asyncFlowRequest.getBusinessSerialId());
            }

            asyncFlowLogEntity.setLogTime(LocalDateTimeUtil.format(asyncFlowRequest.getRequesterTime(), DatePattern.NORM_DATETIME_MS_PATTERN));
            asyncFlowLogEntity.setFlowManagerName(asyncFlowInfo.getFlowManagerName());
            asyncFlowLogEntity.setAsyncFlowModule(asyncFlowRequest.getReqTypeEnumName() + "-" + asyncFlowRequest.getReqTypeEnumMsg());
            asyncFlowLogEntity.setTrackGuid(UUID.randomUUID().toString().toLowerCase().replaceAll("-", ""));
            asyncFlowLogEntity.setAsyncFlowDateTime(asyncFlowRequest.getRequesterTime().toInstant(ZoneOffset.of("+8")).toEpochMilli());

            String resLog = "";
            if (response != null) {
                resLog = JSON.toJSONString(response, SerializerFeature.DisableCircularReferenceDetect);
            }
            asyncFlowLogEntity.setRequest(JSON.toJSONString(request, SerializerFeature.DisableCircularReferenceDetect));
            asyncFlowLogEntity.setResponse(resLog);
            asyncFlowLogEntity.setStatus(allNodeIsSuccess);
            asyncFlowLogEntity.setLogInfo(uptimeDes);
            asyncFlowLogEntity.setOperator(asyncFlowRequest.getOperateName() + "-" + asyncFlowRequest.getOperateNo());
            asyncFlowLogEntity.setSourceDes(asyncFlowRequest.getSourceDes());
            asyncFlowLogEntity.setUseTime(uptime);
            List<AsyncFlowNodeLogEntity> orderFlowLogs =
                    asyncFlowBaseContext.getAsyncFlowNodeLogEntityList().stream().sorted(Comparator.comparing(AsyncFlowNodeLogEntity::getLogTime)).collect(Collectors.toList());
            asyncFlowLogEntity.setAsyncFlowNodeLogEntityList(orderFlowLogs);

            String mongoDBConfig = asyncFlowInfo.getMongoDBConfigStr();

            //环境
            String evn = asyncFlowInfo.getEvn();
            asyncFlowLogEntity.setEnvironments(evn);
            //region 保存MongoDB
            String tableName = asyncFlowInfo.getMongoDBTableName();
            String tableExtName = "_" + LocalDateTime.now().getYear() + "_" + LocalDateTime.now().getMonthValue();
            if (StringUtils.isBlank(asyncFlowInfo.getEsURL())) {
                MongoHelper<AsyncFlowLogEntity> mongoHelper;
                assert mongoDBConfig != null;
                if (mongoDBConfig.equals("close") || StringUtils.isEmpty(mongoDBConfig)) {
                    return;
                } else {
                    mongoHelper = new MongoHelper<>(mongoDBConfig, asyncFlowInfo.getMongoDBName(), tableName + tableExtName);
                }
                Document document = mongoHelper.toDocument(asyncFlowLogEntity);
                mongoHelper.insertOne(document);
                mongoHelper.closeMongoClient();
            } else {
                addLogToEs(asyncFlowInfo, JSONObject.toJSONString(asyncFlowLogEntity, SerializerFeature.DisableCircularReferenceDetect));
            }

            //endregion

            //region 发送超时报警
            if (asyncFlowInfo.getTimeOutsLimit() <= 0) {
                asyncFlowInfo.setTimeOutsLimit(6000);
            }

            if (StringUtils.isNotBlank(asyncFlowInfo.getFlowMaintainName()) && uptime > asyncFlowInfo.getTimeOutsLimit()) {
                String serialid = asyncFlowRequest.getBusinessSerialId();
                if (StringUtils.isBlank(serialid)) {
                    String dateGuid = LocalDateTimeUtil.format(asyncFlowRequest.getRequesterTime(), DatePattern.PURE_DATETIME_PATTERN);
                    ;
                    ;
                    serialid = asyncFlowRequest.getReqTypeEnumMsg() + "-" + dateGuid;
                }
                String stringBuilder = "【节点超时报警[" + asyncFlowInfo.getTimeOutsLimit() + "ms]】" +
                        "\n节点：" + asyncFlowRequest.getReqTypeEnumName() + "--" + asyncFlowRequest.getReqTypeEnumMsg() +
                        "\n来源：" + asyncFlowRequest.getSourceDes() +
                        "\n单号：" + serialid +
                        "\n时间：" + LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN) +
                        "\n环境：" + asyncFlowInfo.getEvn() +
                        "\n站点：" + asyncFlowInfo.getAppUK() +
                        "\n耗时：" + uptime +
                        "\n明细：" + uptimeDes;
                AsyncFlowSendMsg.sendQYWXMsg(asyncFlowInfo.getFlowMaintainName(), stringBuilder, asyncFlowInfo.getNodeArmUrl());
            }
            //endregion

            //异常请求，没有唯一标示
            if (errorRequest) {
                AsyncFlowSendMsg.sendAlarmMsg(asyncFlowRequest, "请求参数中唯一号BusinessSerialId为空", "SaveExecuteTrackLogs", asyncFlowBaseContext.getAsyncFlowInfo());
            }
        } catch (Exception e) {
            String msg = AsyncFlowSendMsg.buildErrorMsg(asyncFlowRequest, e, "SaveExecuteTrackLogs", asyncFlowInfo);
            AsyncFlowSendMsg.sendQYWXMsg(asyncFlowInfo.getFlowMaintainName(), msg, asyncFlowInfo.getNodeArmUrl());
        }


    }
    //endregion


    private static void addLogToEs(AsyncFlowInfo asyncFlowInfo, String logJson) {

        String searchResponseJsonObject = AsyncFlowHttpClientUtil.post(asyncFlowInfo.getEsURL() + asyncFlowInfo.getEsIndexName() + "/_doc", logJson, String.class);
    }
}
